/****************************************************************
 *  This file created by writerB.php on 03/26/2007 at 15:49:22  *
 ****************************************************************
 *  writerB.php created by Tonk (tonk@ctonk.com) in 2007        *
 ****************************************************************
 *                 General Document Information
 *
 *  File Name: world.cpp
 *  Class Name: World
 *  Data members: 3
 *  Notes:
 ****************************************************************/

#include <fstream>
#include <iostream>
#include <vector>
#include "combined.h"
#include "commandTable.h"
#include "definitions.h"
#include "help.h"
#include "log.h"
#include "loginHandler.h"
#include "timestamp.h"
#include "stringutil.h"
#include "world.h"



// Default constructor...
World::World() {
	setExists( false );
	setIndex( 0 );
	setVersion( "unloaded" );
	setBooted( Timestamp::Instance().getDateTime() );
}

// Copy constructor (Doesn't change _WorldID)...
World::World( const World& ref ) {
	setExists( ref.getExists() );
	setIndex( ref.getIndex() );
	setVersion( ref.getVersion() );
}

// Destructor...
World::~World() {
	for ( std::map<std::string,Area*>::iterator it = getAreas().begin(); getAreas().size(); it = getAreas().begin() ) {
		delete it->second;
		getAreas().erase( it );
	}
	for ( std::vector< Timer* >::iterator it = getTimers().begin(); it != getTimers().end(); ) {
		delete (*it);
		it = getTimers().erase( it );
	}
	for ( std::vector< Board* >::iterator it = getBoards().begin(); it != getBoards().end(); ) {
		delete (*it);
		it = getBoards().erase( it );
	}
	for ( std::vector< Help* >::iterator it = getHelpList().begin(); it != getHelpList().end(); ) {
		delete (*it);
		it = getHelpList().erase( it );
	}
}

// Public accessor methods...
void World::setExists( const bool& exists ) { // public
	_exists = exists;
	return;
}

bool World::getExists( void ) const { // public
	return _exists;
}

void World::setIndex( const unsigned& index ) { // public
	_index = index;
	return;
}

unsigned World::getIndex( void ) const { // public
	return _index;
}

void World::setVersion( const std::string& version ) { // public
	_version = version;
	return;
}

std::string World::getVersion( void ) const { // public
	return _version;
}

void World::setBooted( const std::string& booted ) { // public
	_booted = booted;
	return;
}

std::string World::getBooted( void ) const { // public
	return _booted;
}

std::map<std::string,Area*>& World::getAreas( void ) { // public
	return _areas;
}

std::map<std::string,Character*>& World::getCharacters( void ) { // public
	return _characters;
}

std::multimap<std::string,Mob*>& World::getMobs( void ) { // public
	return _mobs;
}

std::multimap<std::string,Object*>& World::getObjects( void ) { // public
	return _objects;
}

std::vector< Timer* >& World::getTimers( void ) { // public
	return _timers;
}

std::vector< Board* >& World::getBoards( void ) { // public
	return _boards;
}

std::vector< Help* >& World::getHelpList( void ) { // public
	return _helpList;
}

std::vector< std::string >& World::getBanners( void ) { // public
	return _banners;
}

// Protected methods...
void World::load( void ) { // protected

	loadServerData();
	loadAreas();
	loadTimers();
	loadHelp();
	loadBoards();
	loadBanners();

	return;
}

void World::loadServerData( void ) { // protected
	std::string foo, version, index, ip;

	// Read data from the system data file...
	std::ifstream read( DATA_FILE );
	if ( read.fail() ) {
		std::string message;
		message << "Server couldn't open " << DATA_FILE << " for writing.";
		log( message, SERVER );
		exit( EXIT_CANT_OPEN_FILE );
	}
	read >> foo >> version;
	read >> foo >> index;
	read.close();
	read.clear();
	setVersion( version );
	setIndex( (unsigned)atoi( index.c_str() ) );

	// Read data from the system ban file...
/*	read.open( BAN_FILE );
	if ( read.fail() ) {
		log( "No system ban file.", SERVER );
	} else {
		log( "System ban file found.", SERVER );
		while ( read >> ip ) {
			;//addBannedIp( ip );
		}
	}
	read.close();
	read.clear();
*/
	return;
}

void World::loadAreas( void ) { // protected
	Area* area = NULL;
	char temp[500];

	// Read in master area file...
	std::ifstream read( AREA_FILE );
	if ( read.fail() ) {
		std::string message;
		message << "Couldn't open " << AREA_FILE << " for reading in World::loadAreas()";
		log( message, SERVER );
		exit( EXIT_CANT_OPEN_FILE );
	}
	while ( read.peek() != EOF ) {
		read.getline( temp, 500 );
		area = new Area( temp );
		add( area );
	}
	read.close();
	read.clear();

	// Read in individual areas...
	for ( std::map<std::string,Area*>::iterator it = getAreas().begin(); it != getAreas().end(); ) {
		read.open( it->second->getFile().c_str() );
		if ( read.fail() ) {
			std::map<std::string,Area*>::iterator temp = it;
			std::string message;
			message << "Couldn't open " << it->second->getFile() << " for reading in World::loadAreas()";
			log( message, SERVER );
			getAreas().erase( it );
			it = temp++;
			continue;
		} else {
			read >> it->second;
			++it;
		}
		read.close();
		read.clear();
	}

	log( "World::Load()", WORLD, false );

	return;
}

void World::loadTimers( void ) { // protected
	schedule( new Tick );
	schedule( new Autosave );
	schedule( new Autobackup );
	schedule( new Autoreboot );
	return;
}

void World::loadHelp( void ) { // protected
	int number = 0;
	char tmp[10000];
	Help* help = NULL;
	std::ifstream read;

	// First we'll set up help files for each Command...
	for ( std::vector< Command* >::iterator it = CommandTable::Instance().getCommands().begin(); it != CommandTable::Instance().getCommands().end(); ++it ) {
		help = addHelp();
		help->setTitle( (*it)->getName() );
		help->setTags( (*it)->getTags() );
		if ( (*it)->getHelp().empty() )
			help->setText( (*it)->getSyntax() );
		else
			help->setText( (*it)->getSyntax() + "\n\r\n\r" + (*it)->getHelp() );
		help->setLevel( (*it)->getLevel() );
	}

	// Now we'll read in our help file...
	read.open( HELP_FILE );
	if ( read.fail() ) {
		std::string message;
		message << "Couldn't open " << HELP_FILE << ".\n";
		log( message, SERVER );
		return;
	}

	read >> number; // How many helps are there?
	read.get(); // Kill the extra newline.

	// Read 'em in and line 'em up...
	for ( int i=0; i < number; i++ ) {
		help = addHelp();
		read.getline( tmp, 10000, '~' );
		help->setTitle( std::string(tmp) );
		read.getline( tmp, 1000, '~' );
		help->setTags( std::string(tmp) );
		read.getline( tmp, 10000 );
		help->setText( std::string(tmp) );
	}

	read.close();
	read.clear();

	return;
}

void World::loadBoards( void ) { // protected

	addBoard()->initialize( ADMIN_BOARD,    115, "Admin" );
	addBoard()->initialize( GENERAL_BOARD,  1,   "General" );
	addBoard()->initialize( IDEA_BOARD,     1,   "Idea" );
	addBoard()->initialize( PERSONAL_BOARD, 1,   "Personal" );

	for ( std::vector< Board* >::iterator it = getBoards().begin(); it != getBoards().end(); it++ )
		(*it)->load();

	return;
}

void World::loadBanners( void ) { // protected
	std::string file;
	std::string banner;
	char temp[1000];
	int numberBanners = 0;

	// How many banners are there?
	file << BANNER_DIR << BANNER_FILE;
	std::ifstream read( file.c_str() );
	read >> numberBanners;
	read.close();
	read.clear();

	// Read in each one...
	for ( int i=0; i < numberBanners; i++ ) {
		file = BANNER_DIR;
		file << i+1 << ".sym";
		read.open( file.c_str() );
		if ( read.fail() ) {
			// error
			continue;
		}
		while ( read.peek() != EOF ) {
			read.getline( temp, 1000 );
			banner << temp << "\n\r";
		}
		getBanners().push_back( banner );
		banner.clear();
		file.clear();
		read.close();
		read.clear();
	}

	return;
}

void World::saveCharacters( void ) { // protected

	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); ++it )
		if ( !it->second->save() )
			log( "Character::save() returned false for " + it->second->getName() + ".", SERVER );

	return;
}

void World::saveAreas( void ) { // protected
	std::ofstream master_file( AREA_FILE );
	std::ofstream area_file;

	for ( std::map<std::string,Area*>::iterator it = getAreas().begin(); it != getAreas().end(); ++it ) {
		master_file << it->second->getKeyword() << '\n';
		area_file.open( it->second->getFile().c_str() );
		if ( area_file.fail() ) {
			log( "Couldn't open " + it->second->getFile() + " for writing.", WORLD );
			log( "Couldn't open " + it->second->getFile() + " for writing.", SERVER, false );
		} else {
			area_file << it->second;
		}
		area_file.close();
		area_file.clear();
	}

	master_file.close();
	master_file.clear();

	return;
}

void World::saveServerData( void ) { // protected

	// Write to the system data file...
	std::ofstream write( DATA_FILE );
	if ( write.fail() ) {
		std::string message;
		message << "Couldn't open " << DATA_FILE << " for writing.";
		log( message, SERVER );
	} else {
		write << "version:\t" << _version << '\n';
		write << "index:\t\t" << _index << '\n';
		write << "nIndex:\t" << Note::noteIndex << '\n';
	}
	write.close();
	write.clear();

	// Write to the system ban file (if there are any banned names or ips)...
/*	if ( !getBanList().empty() ) {
		std::list< std::string* >::iterator it;
		write.open( BAN_FILE );
		for ( it = getBanList().begin(); it != getBanList().end(); it++ )
			write << *(*it) << '\n';
		write.close();
		write.clear();
	}
*/
	return;
}

void World::flushOutput( void ) { // protected
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); ++it )
		it->second->flushOutput();
	return;
}

void World::updateCharacters( void ) { // protected
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); )
		if ( it->second ) {
			if ( it->second->getSocket()->GotInput() == true )
				it->second->setGotInput( true );
			if ( it->second->getSocket()->IsDisconnected() )
				it->second->setDisconnected( true );
			++it;
		} else
			getCharacters().erase( it );

	killFlaggedCharacters();

	return;
}

void World::updateMobs( void ) { // protected
	std::map<std::string,Mob*>::iterator it;
	for ( std::map<std::string,Mob*>::iterator it = getMobs().begin(); it != getMobs().end(); it++ )
		it->second->update();

	return;
}

void World::killFlaggedCharacters( void ) { // protected
	Character* c2 = NULL;
	std::map<std::string,Character*>::iterator temp;

	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); ) {
		c2 = it->second;
		if ( c2->getDisconnected() ) {
			if ( c2->getRoom() ) // This will still be NULL after a bad login attempt.
				c2->getRoom()->drop( c2 );
			temp = ++it;
			--it;
			getCharacters().erase( it );
			omninet( c2->getName() + " disconnected from " + c2->getSocket()->GetSocketIP() );
			delete c2;
			c2 = NULL;
			it = temp;
		} else {
			++it;
		}
	}

	return;
}

void World::handleInput( void ) { // protected
	std::string thisInput;
	bool repeat = false;
	// This is for handling input; it loops through Characters,
	// checks to see if there's input to play with, then goes on that.
	if ( !getCharacters().empty() ) {
		for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); ++it ) {
			// Don't mess with input until delayed text is finished
			if ( it->second->getGotInput() == true /*&& !(*it)->getWaiting()*/ ) {
				if ( !it->second->getSocket()->GetInput().empty() ) {
					thisInput = trim( it->second->getSocket()->GetInput() );
					if ( str_prefix( "!", thisInput ) ) { // A single ! marks a "repeat" command.
						repeat = true;
						thisInput = it->second->getLastInput();
					}
					if ( it->second->getTedit() ) {
						tCommandTable::Instance().execute( it->second, thisInput );
					} else {
						switch ( it->second->getStatus() ) {
							case LOGIN:	LoginList::Instance().act( it->second, thisInput ); break;
							case AEDIT:	aCommandTable::Instance().execute( it->second, thisInput ); break;
							case MEDIT: mCommandTable::Instance().execute( it->second, thisInput ); break;
							case PEDIT: pCommandTable::Instance().execute( it->second, thisInput ); break;
							case REDIT: rCommandTable::Instance().execute( it->second, thisInput ); break;
							default: CommandTable::Instance().execute( it->second, thisInput ); break;
						}
					}
					if ( !repeat ) // If we didn't have a "repeat" command, refresh the lastInput
						it->second->setLastInput( thisInput );
					// We need to clean the inbuf or it'll loop using the same stuff...
					it->second->getSocket()->FlushInput();
				}
			}
		}
	}

	updateCharacters();

	return;
}

void World::updateTimers( void ) { // protected

	if ( !getTimers().empty() ) {
		for ( std::vector< Timer* >::iterator it = getTimers().begin(); it != getTimers().end(); ) {
			if ( !(*it)->fire() || !(*it)->getEnabled() ) // Timer::Fire() returns true unless the timer needs to be killed
				it = getTimers().erase( it );
			else
				it++;
		}
	}

	return;
}

void World::schedule( Timer* timer ) { // protected
	timer->setEnabled( true );
	getTimers().push_back( timer );
	return;
}

bool World::unschedule( Timer* timer ) { // protected
 	timer->setEnabled( false );
	for ( std::vector< Timer* >::iterator it = getTimers().begin(); it != getTimers().end(); it++ )
		if ( (*it) == timer ) {
			getTimers().erase( it );
			return true;
		}
	return false;
}

void World::setConnects( unsigned long connects ) { // protected
	_connects = connects;
	return;
}

unsigned long World::getConnects( void ) { // protected
	return _connects;
}

std::string World::connectionNumber2string( void ) { // protected
	char* temp = new char[10];
	setConnects( getConnects()+1 );
	sprintf( temp, "%lu", getConnects() );
	return (std::string)temp;
}

// General methods...
void World::exist( const std::string &startMessage, const int& port, const bool& copyover ) { // public
	std::string buf;

	load();

	// Check for a copyover...
	if ( copyover == false ) {
		SocketServer::Instance().Start();
	} else {
		copyoverRecovery();
	}

	// Send a start message to the terminal...
	std::cout << "\n\t" << startMessage << " on port " << port;
	std::cout << "\n\tTime: " << Timestamp::Instance().getDateTime() << "\n\n";

	// Log all the action...
	buf << "Symphony booted on port " << port;
	log( buf, SERVER, false );
	log( "World::exist()", WORLD, false );

	setExists( true );
	setBooted( Timestamp::Instance().getDateTime() );

	// World::_exists can be set to false by shutdown/copyover
	while( getExists() ) {
		monitor();
	}

	// Save...
	save();

	std::cout << "Shutdown successful.\n";
	return;
}

void World::monitor() { // public

    flushOutput();

    if ( SocketServer::Instance().Monitor() ) {
		Character* character = add( new Character, true );
		character->setSocket( SocketServer::Instance()._socketList.back() );
		character->setStatus( LOGIN );
		character->Send( randomBanner() );
		character->Send( "{xname: " );
	}

	updateTimers();
	updateMobs();
	updateCharacters();
	handleInput();
}

void World::copyover() { // public
	Character* dead;
	std::string controlSock;
	std::string port;
	std::map<std::string,Character*>::iterator temp;

	// Let's not forget this...
	save();

	// Open our temporary copyover file...
	std::ofstream copyover_file( COPYOVER_FILE );
	if ( copyover_file.fail() ) {
		std::string message;
		message << "Couldn't open " << COPYOVER_FILE << " for writing.\n";
		log( message, SERVER );
		return;
	}

	// Remove Characters with "incomplete" connections, and save the rest to the copyover file...
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); ) {
		// These poor suckers won't make the cut...
		if ( it->second->getStatus() < CONNECTED ) {
			dead = it->second;
			temp = ++it;
			--it;
			getCharacters().erase( it );
			delete dead;
			continue;
			it = temp;
		}
		//it->second->cSave();
		copyover_file	<< it->second->getSocket()->GetDescriptor() << ' '
						<< it->second->getName() << ' '
						<< it->second->getSocket()->GetSocketIP() << ' '
						<< it->second->getSocket()->GetSocketHost() << '\n';
		it++;
	}

	// Close out the copyover file...
	copyover_file << "-1";
	copyover_file.close();

	// Get some Socket info...
	controlSock << SocketServer::Instance().GetHostSocket();
	log( "Copyover initiated.", SERVER );

	// Start the new process...
	port << DEFAULT_PORT;
	execl( "./symphony", port.c_str(), "-copyover", controlSock.c_str(), NULL );

	// Theoretically, World::copyover() should never return, because execl() starts the new
	// process and then kills the current process.  But, if execl() can't do it's job, it
	// shouldn't hurt anything, because World::copyover() will just return control back to
	// the calling function.
	std::string message;
	message	<< "MAJOR ERROR\n"
			<< "\t - execl() returned control back to World::copyover()\n"
			<< "\t   without spawning a new process.\n"
			<< "\t - This could be because there is no symphony binary\n"
			<< "\t   executable in the symphony home directory.\n"
			<< "\t - The server will keep running, however any further calls\n"
			<< "\t   to World::copyover() will probably keep failing.\n"
			<< "\t - Suggestion: rebuild the server (i.e. run \"make\")\n";
	log( message, SERVER );
	World::Instance().broadcast( "Copyover aborted." );

	return;
}

void World::copyoverRecovery() { // public
	Character* character = NULL;
	int desc = 0;
	std::string name, host, lhost;

	// Open up our temp file...
	std::ifstream copyover_file( COPYOVER_FILE );
	if ( copyover_file.fail() ) {		
		std::string message;
		message << "Could not open " << COPYOVER_FILE << " for reading.  Aborting copyover and killing process.";
		log( message, SERVER );
		exit( EXIT_BAD_COPYOVER );
	}

	// Read in our character/socket data and repopulate lists...
	for(;;) {
		copyover_file >> desc >> name >> host >> lhost;
		if ( desc == -1 )
			break;
		character = new Character( true );
		character->getSocket()->SetDescriptor( desc );
		character->getSocket()->SetSocketIP( host );
		character->getSocket()->SetSocketHost( lhost );
		character->setName( name );
		character->load();
		character->setStatus( CONNECTED );
		// Add user to socket and character lists...
		SocketServer::Instance()._socketList.push_back( character->getSocket() );
		add( character );
		// We're all done. Let them know. :)
		character->Send( "Copyover successful." );
	}

	// Clean up our ofstream and temp file...
	copyover_file.close();
	remove( COPYOVER_FILE );
	log( "Copyover recovery successful.", SERVER );

	return;
}

void World::die() { // public
	log( "Symphony shutting down.", SERVER );
	log( "World::die()", WORLD );
	setExists( false );
}

void World::broadcast( const std::string& message ) { // public
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
		if ( it->second->getStatus() >= CONNECTED )
			it->second->Send( message );
	return;
}

void World::omninet( const std::string& message, Character* character, const bool& logThis ) { // public
	std::string buf = message;

	if ( logThis )
		log( message, OMNINET );
	buf = " {W>{C>{c>{x ";
	buf << message;

	if ( character != NULL ) {
		for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
			if ( it->second->getOmninet() && character != it->second && character->getLevel() <= it->second->getLevel() && it->second->getStatus() >= CONNECTED )
				it->second->Send( buf );
		return;
	} else {
		for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
			if ( it->second->getOmninet() && it->second->getStatus() >= CONNECTED )
				it->second->Send( buf );
		return;
	}

	return;
}

void World::save( void ) { // public

	saveServerData();
	saveAreas();
	saveCharacters();

	return;
}

Area* World::add( Area* area ) { // public
	getAreas()[area->getKeyword()] = area;
	return area;
}

Area* World::getArea( const std::string& keyword ) { // public
	for ( std::map<std::string,Area*>::iterator it = getAreas().begin(); it != getAreas().end(); it++ )
		if ( str_prefix( lowerString( keyword ), lowerString( it->second->getKeyword() ) ) )
			return it->second;
	return NULL;
}

Area* World::getParentArea( const unsigned& vnum ) { // public
	for ( std::map<std::string,Area*>::iterator it = getAreas().begin(); it != getAreas().end(); it++ )
		if ( it->second->getFirstv() <= vnum && it->second->getLastv() >= vnum )
			return it->second;
	return NULL;
}

bool World::drop( Area* area ) { // public
	for ( std::map<std::string,Area*>::iterator a_it = getAreas().begin(); a_it != getAreas().end(); a_it++ )
		if ( (*a_it).second == area ) {
			getAreas().erase( a_it );
			return true;
		}
	return false;
}

Room* World::getRoom( const unsigned& vnum ) { // public
	for ( std::map<std::string,Area*>::iterator a_it = getAreas().begin(); a_it != getAreas().end(); ++a_it )
		for ( std::vector< Room* >::iterator r_it = (*a_it).second->getRooms().begin(); r_it != (*a_it).second->getRooms().end(); ++r_it )
			if ( (*r_it)->getVnum() == vnum )
				return *r_it;
	return NULL;
}

Character* World::add( Character* character, bool newConnection ) { // public
	if ( newConnection ) {
		getCharacters()[connectionNumber2string()] = character;
	} else {
		getCharacters()[character->getName()] = character;
	}
	return character;
}

Character* World::getCharacter( const std::string& name ) { // public
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
		if ( str_prefix( lowerString( name ), lowerString( it->second->getName() ) ) )
			return it->second;
	return NULL;
}

bool World::dropCharacter( const std::string& name ) { // public
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
		if ( str_prefix( name, it->second->getName() ) ) {
			getCharacters().erase( it );
			return true;
		}
	return false;
}

bool World::drop( Character* character ) { // public
	for ( std::map<std::string,Character*>::iterator it = getCharacters().begin(); it != getCharacters().end(); it++ )
		if ( it->second == character ) {
			getCharacters().erase( it );
			return true;
		}
	return false;
}

Mob* World::add( Mob* mob ) { // public
	getMobs().insert( make_pair( mob->getName(), mob ) );
	return mob;
}

Mob* World::getMob( const std::string& name ) { // public
	for ( std::map<std::string,Mob*>::iterator it = getMobs().begin(); it != getMobs().end(); it++ )
		if ( str_prefix( lowerString( name ), lowerString( it->second->getName() ) ) )
			return it->second;
	return NULL;
}

bool World::drop( Mob* mob ) { // public
	for ( std::map<std::string,Mob*>::iterator it = getMobs().begin(); it != getMobs().end(); it++ )
		if ( it->second == mob ) {
			getMobs().erase( it );
			return true;
		}
	return false;
}

Object* World::add( Object* object ) { // public
	getObjects().insert( make_pair( object->getName(), object ) );
	return object;
}

Object* World::getObject( const std::string& name ) { // public
	for ( std::multimap<std::string,Object*>::iterator it = getObjects().begin(); it != getObjects().end(); it++ )
		if ( str_prefix( lowerString( name ), lowerString( it->second->getName() ) ) )
			return it->second;
	return NULL;
}

bool World::drop( Object* object ) { // public
	for ( std::multimap<std::string,Object*>::iterator it = getObjects().begin(); it != getObjects().end(); it++ )
		if ( it->second == object ) {
			getObjects().erase( it );
			return true;
		}
	return false;
}

Board* World::addBoard( void ) { // public
	Board* board = new Board;
	getBoards().push_back( board );
	return board;
}

Help* World::addHelp( void ) { // public
	Help* help = new Help;
	getHelpList().push_back( help );
	return help;
}

std::string World::getHelp( const std::string& query, const unsigned& level ) { // public
	std::string search = trim( query );
	std::string text;

	if ( search.empty() ) {
		text << "GENERIC HELP STUFF GOES HERE";
	} else
		for ( std::vector< Help* >::iterator it = getHelpList().begin(); it != getHelpList().end(); ++it )
			if ( strstr( (*it)->getTags(), search ) && level >= (*it)->getLevel() )
				text << (*it);
	if ( text.empty() )
		text << "No help found for \"" << search << "\"";
	return text;
}

std::string World::randomBanner( void ) { // public
	int banner = Timestamp::Instance().random( 1, getBanners().size() );
	std::vector< std::string >::iterator it;

	it = getBanners().begin();
	for ( int i=0; i < banner && it != getBanners().end(); i++ )
		it++;

	return (*it);
}

// Static methods...

// Associated operators...
std::ostream& operator << ( std::ostream& write, const World& world ) { // associated
	write << "Server version:\t" << world.getVersion() << '\n';
	write << "Booted:\t\t" << world.getBooted() << '\n';
	return write;
}

std::ostream& operator << ( std::ostream& write, const World* world ) { // associated
	write << *world;
	return write;
}

